using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
using Tomahawk.Runtime.Input;
using Tomahawk.Runtime.Scene;
using Tomahawk.Runtime.Rendering;
using Tomahawk.Runtime.Logic;
using Tomahawk.Runtime.Canvas;
using Tomahawk.Runtime.Audio;
using Tomahawk.Runtime.Debug;
using Tomahawk.Runtime.Rendering.Objects;
using Tomahawk.Runtime.Threading;
using Tomahawk.Runtime.Rendering.Materials;
using Tomahawk.Runtime.Physics;
using Tomahawk.Runtime.Physics.Interfaces;
using Tomahawk.Runtime.Resources;
using Tomahawk.Runtime.Player;
using Microsoft.Xna.Framework.GamerServices;
using Tomahawk.Runtime.Storage;
using Tomahawk.Runtime.LoadingScreens;
using Tomahawk.Runtime.Components;
using Tomahawk.Runtime.Utils;
using Tomahawk.Runtime.Networking;
using Tomahawk.Runtime.Localization;
using Tomahawk.Runtime.Rendering.Particles;
using Tomahawk.Runtime.Console;
using Tomahawk.Runtime.CrashDump;

namespace Tomahawk.Runtime
{
    /// <summary>
    /// This heart of the engine. It's implemented as an XNA Game class, 
    /// so it will receive initialization and update events. This class is 
    /// a singleton, so you can easily access any member from within your game.
    /// </summary>
    public class Engine : Game
    {
        #region Profiling
        static Profile UpdateSection = Profile.Get("Engine.Update");
        static Profile DrawSection = Profile.Get("Engine.Draw");
        #endregion

        #region "Engine Events"

        public delegate void EngineNotification();
        public event EngineNotification OnBeforeInitialize;
        public event EngineNotification OnAfterInitialize;
        public event EngineNotification OnTrialUnlock;

        public delegate void UpdateNotification(float elapsedTime);
        public event UpdateNotification OnBeforeUpdate;
        public event UpdateNotification OnAfterUpdate;

        public delegate void CustomRenderingDelegate(GameTime gameTime, GraphicsDevice gd);
        public event CustomRenderingDelegate OnCustomRendering;

        public delegate void GameStateDelegate(GameState gameState);
        public event GameStateDelegate OnBeforeGamestateUnload;

        #endregion

        #region Global Resource Lock
        public static object ResourceLock 
        { 
            get 
            { 
                return Engine.Instance.GraphicsDevice; 
            } 
        }
        #endregion

        #region "Singleton"

        /// <summary>
        /// Instance of the engine
        /// </summary>
        private static Engine instance = null;

        /// <summary>
        /// Use this static property to access the engine object
        /// </summary>
        public static Engine Instance
        {
            get { return instance; }
            set { instance = value; }
        }

        /// <summary>
        /// Static constructor. Create the singleton instance
        /// </summary>
        static Engine()
        {
            //create the singleton instance
            Engine.instance = new Engine();
        }

        #endregion

        #region "Configuration Propierties"

        private Type engineCofigType = typeof(XEngineConfiguration);

        private String engineConfigArchetype = @"Tomahawk.Content\Config\EngineConfig.xml";

        /// <summary>
        /// Sets the type of the Engine configuration object. 
        /// It allows you use a derived type to extend the config file itself.
        /// </summary>
        public Type EngineConfigType
        {
            get { return this.engineCofigType; }
            set { this.engineCofigType = value; }
        }

        /// <summary>
        /// Sets the engine configuration XML archetype. 
        /// It allows you tu use a custom config file for your game.
        /// </summary>
        public String EngineConfigArchetype
        {
            get { return this.engineConfigArchetype; }
            set { this.engineConfigArchetype = value; }
        }

        /*
        private bool disconnectedMode = false;

        public bool DisconnectedMode
        {
            get { return this.disconnectedMode; }
            set { this.disconnectedMode = true; }
        }
        */

        #endregion

        #region "Engine Objects"

        //xna objects
        private GraphicsDeviceManager graphicsDeviceManager = null;

        //engine objects
        private ResourceManager resourceManager = null;
        private InputManager inputManager = null;
        private SceneManager sceneManager = null;
        private CameraManager cameraManager = null;
        private CanvasManager canvasManager = null;
        private PhysicsManager physicsManager = null;
        private AudioManager audioManager = null;
        private RenderManager renderManager = null;
        private MaterialManager materialManager = null;
        private ParticlesManager particlesManager = null;
        private PlayerManager playerManager = null;
        private StorageManager storageManager = null;
        private NetworkManager networkManager = null;
        private LocalizationManager localizationManager = null;

        private Vector2 virtualCanvasScale = Vector2.One;
        private XEngineConfiguration engineConfiguration = null;

        //debug console
        private Debug.Console debugConsole = null;

        //game states
        private GameState currentGameState = null;

        #endregion

        #region "Initialization"

        //private bool flagOnAfterInitPendant = false;

        /// <summary>
        /// Store the previous value of trial mode to check if it has changed
        /// </summary>
        private bool isTrialModeLastValue = false;

        /// <summary>
        /// Engine constructor. 
        /// </summary>
        private Engine()
        {
        }

        /// <summary>
        /// Runs the game
        /// </summary>
        new public void Run()
        {
            try
            {
                //load the engine and game configuration
                this.engineConfiguration =
                    XWorld.Instance.CreateObject(this.engineCofigType, "Configuration", this.engineConfigArchetype)
                    as XEngineConfiguration;

                //create the core XNA objects
                this.graphicsDeviceManager = new GraphicsDeviceManager(this);
                this.graphicsDeviceManager.DeviceReset += new EventHandler(OnDeviceReset);
                this.graphicsDeviceManager.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(OnPreparingDeviceSettings);
                this.graphicsDeviceManager.SynchronizeWithVerticalRetrace = this.engineConfiguration._Video.vsync;

                //create and register the physics service
                if (this.engineConfiguration._Physics.enabled)
                {
#if WITH_JIGLIBX
                    Physics.JigLibX.PhysicsService physService = new Physics.JigLibX.PhysicsService(this);
                    Services.AddService(typeof(IPhysicsService), physService);
#endif
                }

                //set the resolution on the device
#if XBOX
                this.InitGraphicsMode(this.graphicsDeviceManager, 1280, 720, true);
#else
                this.InitGraphicsMode(
                    this.graphicsDeviceManager,
                    this.engineConfiguration._Video.screenWidth,
                    this.engineConfiguration._Video.screenHeight,
                    this.engineConfiguration._Video.fullscreen);
#endif

                // configure the video
                this.virtualCanvasScale.X = (float)this.engineConfiguration._Video.screenWidth / this.engineConfiguration._Video.virtualCanvasWidth;
                this.virtualCanvasScale.Y = (float)this.engineConfiguration._Video.screenHeight / this.engineConfiguration._Video.virtualCanvasHeight;

                if (this.virtualCanvasScale.X != this.virtualCanvasScale.Y)
                {
                    throw new Exception("Unsupported virtual canvas size. Must be multiple of the screen size");
                }

                //add the gamerservices component
                Components.Add(new GamerServicesComponent(this));

                //start in trial mode if specified so
                if (this.engineConfiguration.SimulateTrialMode)
                {
                    Guide.SimulateTrialMode = true;
                }

                //store the initial value of Trial Mode
                this.isTrialModeLastValue = Guide.IsTrialMode;

                base.Run();
            }
            catch (Exception e)
            {
#if XBOX
                if (Engine.Instance.EngineConfiguration._BSOD.Enabled)
                {
                    CrashDebugGame game = new CrashDebugGame(e, Engine.Instance.GraphicsDevice, Engine.Instance.Content);
                    game.Run();
                }
#endif
                throw e;
            }
        }

        void OnPreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
        {
            PresentationParameters pp = e.GraphicsDeviceInformation.PresentationParameters;
            MultiSampleType multisampling = Engine.Instance.EngineConfiguration._Video.multisampling;

#if XBOX
            pp.MultiSampleQuality = 0;
            pp.MultiSampleType = multisampling;
            return;
#else
            int quality = 0;
            GraphicsAdapter adapter = e.GraphicsDeviceInformation.Adapter;
            SurfaceFormat format = adapter.CurrentDisplayMode.Format;

            if (adapter.CheckDeviceMultiSampleType(DeviceType.Hardware, format,
                false, multisampling, out quality))
            {
                // even if a greater quality is returned, we only want quality 0
                pp.MultiSampleQuality = 0;
                pp.MultiSampleType = multisampling;
            }
            else
            {
                pp.MultiSampleQuality = 0;
                pp.MultiSampleType = MultiSampleType.None;
            }
            return;
#endif
        }

        void OnDeviceReset(object sender, EventArgs e)
        {
            if (this.RenderManager != null && this.GraphicsDevice != null)
            {
                this.RenderManager.ReloadSurfaces(this.GraphicsDevice);
            }
        }

        /// <summary>
        /// Engine Initialization
        /// </summary>
        protected override void Initialize()
        {
            //debug console, to be ready before anything else
            debugConsole = new Tomahawk.Runtime.Debug.Console();

            // configure the game console
            ConsoleComponent.Register(this, ref engineConfiguration._Console);
            ConsoleComponent.WriteLine("###", Color.LightGreen);
            ConsoleComponent.WriteLine("### Tomahawk Engine Console", Color.LightGreen);
            ConsoleComponent.WriteLine("###", Color.LightGreen);
            ConsoleComponent.WriteLine("", Color.LightGreen);
            ConsoleComponent.Prompt(">> ", HandleConsoleInput);

            //
            //create the core Engine subsystem managers
            //

            this.sceneManager = new SceneManager();
            if (this.engineConfiguration._Physics.enabled)
            {
                this.physicsManager = new PhysicsManager();
            }
            this.canvasManager = new CanvasManager(this.GraphicsDevice);
            this.inputManager = new InputManager();
            this.cameraManager = new CameraManager();
            this.audioManager = new AudioManager(this.engineConfiguration._Audio.settings);
            this.resourceManager = new ResourceManager(this.GraphicsDevice);
            this.playerManager = new PlayerManager();
            this.renderManager = new RenderManager();
            this.materialManager = new MaterialManager();
            this.particlesManager = new ParticlesManager();
            this.networkManager = new NetworkManager();
            this.localizationManager = new LocalizationManager();

            //create the storage device manager
            this.storageManager = new StorageManager(this);

            //create the default camera
            Camera defCamera =
                Engine.Instance.SceneManager.CreateCamera(
                    this.graphicsDeviceManager.GraphicsDevice.Viewport, "default");

            //Initialize shadow map
            //this.SetupShadowMap(this.graphicsDeviceManager.GraphicsDevice);

            //inform the application
            if (this.OnBeforeInitialize != null) OnBeforeInitialize();

            // configure the time loop
            this.IsFixedTimeStep = this.engineConfiguration._GameLoop.FixedStep;
            this.TargetElapsedTime = TimeSpan.FromSeconds(this.engineConfiguration._GameLoop.TargetElapsed);

            // configure the overlays
            FramerateComponent.Register(this, ref this.engineConfiguration._Overlay.framerate);
#if !XBOX
            ProfilerComponent.Register(this, ref this.engineConfiguration._Overlay.profiler);
#endif
            FreeCamComponent.Register(this, ref this.engineConfiguration._Overlay.freeCam);
            InfoComponent.Register(this, ref this.engineConfiguration._Overlay.info);

            //initialize base Game class (the xna stuff)
            base.Initialize();

            //set a default ambient color
            this.AmbientColor = new Color(1.0f, 1.0f, 1.0f, 1.0f);

            //call the after initialize method
            if (this.OnAfterInitialize != null) this.OnAfterInitialize();
        }

        /// <summary>
        /// Attempt to set the display mode to the desired resolution.  Itterates through the display
        /// capabilities of the default graphics adapter to determine if the graphics adapter supports the
        /// requested resolution.  If so, the resolution is set and the function returns true.  If not,
        /// no change is made and the function returns false.
        /// </summary>
        /// <param name="iWidth">Desired screen width.</param>
        /// <param name="iHeight">Desired screen height.</param>
        /// <param name="bFullScreen">True if you wish to go to Full Screen, false for Windowed Mode.</param>
        private bool InitGraphicsMode(GraphicsDeviceManager graphics, int iWidth, int iHeight, bool bFullScreen)
        {
            // If we aren't using a full screen mode, the height and width of the window can
            // be set to anything equal to or smaller than the actual screen size.
            if (bFullScreen == false)
            {
                if ((iWidth <= GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width)
                    && (iHeight <= GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height))
                {
                    graphics.PreferredBackBufferWidth = iWidth;
                    graphics.PreferredBackBufferHeight = iHeight;
                    graphics.IsFullScreen = bFullScreen;
                    graphics.ApplyChanges();
                    return true;
                }
            }
            else
            {
                // If we are using full screen mode, we should check to make sure that the display
                // adapter can handle the video mode we are trying to set.  To do this, we will
                // iterate thorugh the display modes supported by the adapter and check them against
                // the mode we want to set.
                foreach (DisplayMode dm in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
                {
                    // Check the width and height of each mode against the passed values
                    if ((dm.Width == iWidth) && (dm.Height == iHeight))
                    {
                        // The mode is supported, so set the buffer formats, apply changes and return
                        graphics.PreferredBackBufferWidth = iWidth;
                        graphics.PreferredBackBufferHeight = iHeight;
                        graphics.IsFullScreen = bFullScreen;
                        graphics.ApplyChanges();
                        return true;
                    }
                }
            }
            return false;
        }

        #endregion

        #region "Console Input"
        private void HandleConsoleInput(string text)
        {
            if (!ConsoleCommand.IsCommandTableBuilt)
            {
                ConsoleCommand.BuidCommandTable();
            }

            string[] tokens = text.Split(new char[] { ' ', '\t' });

            if (tokens.Length > 0)
            {
                string result = ConsoleCommand.Execute(tokens[0], tokens);
                ConsoleComponent.WriteLine(result, Color.LightYellow);
            }
            ConsoleComponent.Prompt(">> ", HandleConsoleInput);
        }
        #endregion

        #region "Loading"

        protected override void LoadContent()
        {
            base.LoadContent();

            //load engine asset for low level usage
            this.debugConsole.LoadGraphicsContent(this.graphicsDeviceManager.GraphicsDevice, this.Content);

            //load the scene shader used to render the game scene
            RenderManager.SceneShader = MaterialManager.Load<SASSceneEffect>(engineConfiguration._Video.sceneShaderName, this.Content);

            //load the object shader override, if defined
            RenderManager.ObjectShaderOverride = MaterialManager.Load<SASEffect>(engineConfiguration._Video.objectShaderOverrideName, this.Content);
        }

        #endregion

        #region "Rendering"

        /// <summary>
        /// Rendering pseudo stages on which renderable components can render itself
        /// </summary>
        public enum RenderingStages
        {
            Background,
            BeforeScene,
            Scene,
            AfterScene,
            Hud
        }

        private Color clearColor = Color.CornflowerBlue;

        public Color ClearColor
        {
            get { return clearColor; }
            set { clearColor = value; }
        }

        private float clearDepth = 1.0f;

        public float ClearDepth
        {
            get { return clearDepth; }
            set { clearDepth = value; }
        }

        private Color ambientColor = new Color(1.0f, 1.0f, 1.0f, 1.0f);

        /// <summary>
        /// Sets the ambient color
        /// </summary>
        public Color AmbientColor
        {
            get { return ambientColor; }
            set
            {
                ambientColor = value;
                RenderManager.Environment.light0.ambient = value.ToVector4();
            }
        }

        protected override bool BeginDraw()
        {
            if (LoadingScreen.Working && !LoadingScreen.LoadingFinished)
            {
                LoadingScreen.Draw();
                return false;
            }

            return base.BeginDraw();
        }

        protected override void Draw(GameTime gameTime)
        {
            #region Begin Profiling Section
#if PROFILER
            using (IDisposable d = DrawSection.Measure())
            {
#endif
            #endregion

                RenderManager.Environment.time = (float)gameTime.TotalGameTime.TotalSeconds;
                RenderManager.Environment.elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

                GraphicsDevice gd = this.graphicsDeviceManager.GraphicsDevice;

                //tick the prerender event on all renderable objects
                XWorld.Instance.PreRender();

                if (OnCustomRendering != null)
                {
                    OnCustomRendering(gameTime, gd);
                }
                else if (RenderManager.SceneShader != null)
                {
                    SASEffect.currentRenderGroup = SASEffect.RenderGroup.Color;
                    SceneManager.PreRender(gd);
                    RenderManager.PreRender(gd);
                    RenderManager.SceneShader.Effect.DrawScene(gd);
                    RenderManager.PostRender(gd);
                    SceneManager.PostRender(gd);
                }
                else
                {
                    this.graphicsDeviceManager.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, clearColor, 1.0f, 0);
                    SASEffect.currentRenderGroup = SASEffect.RenderGroup.Color;
                    SceneManager.PreRender(gd);
                    SceneManager.Render(gd);
                    SceneManager.PostRender(gd);
                    this.canvasManager.Render();
                }

                //draw the debug console
                if (Engine.Instance.engineConfiguration._Console.enabled)
                {
                    this.debugConsole.Render(gd);
                }

                //draw the loading screen
                if (LoadingScreen.Working && LoadingScreen.LoadingFinished)
                {
                    LoadingScreen.Draw();
                }

                //let the base draw
                base.Draw(gameTime);

                #region End Profiling Section
#if PROFILER
            }
#endif
                #endregion
        }

        #region "Shadow Mapping"

        RenderTarget2D rtShadowMap;
        Texture2D txShadowMap;

        /// <summary>
        /// Allow acess to shadow map texture
        /// </summary>
        public Texture2D ShadowMapTexture
        {
            get { return this.txShadowMap; }
        }

        /// <summary>
        /// Create the shadow map render target
        /// </summary>
        private void SetupShadowMap(GraphicsDevice device)
        {
            rtShadowMap = new RenderTarget2D(device, 512, 512, 1, SurfaceFormat.Color);
        }

        /// <summary>
        /// Render the shadow map
        /// </summary>
        public void RenderShadows(GraphicsDevice device)
        {
            device.RenderState.AlphaBlendEnable = false;
            device.RenderState.AlphaTestEnable = false;
            device.RenderState.DepthBufferEnable = true;
            device.RenderState.DepthBufferFunction = CompareFunction.LessEqual;
            device.RenderState.DepthBufferWriteEnable = true;

            //activate the render target
            device.SetRenderTarget(0, this.rtShadowMap);

            //clear the shadow map in white
            this.graphicsDeviceManager.GraphicsDevice.Clear(Color.White);

            //render in shadow mode
            //this.sceneManager.Render(this.graphicsDeviceManager.GraphicsDevice, true);

            //get the rendered texture
            device.SetRenderTarget(0, null);
            this.txShadowMap = this.rtShadowMap.GetTexture();
        }

        #endregion

        #endregion

        #region "Physics"

        public IPhysicsService PhysService
        {
            get
            {
                return (IPhysicsService)Services.GetService(typeof(IPhysicsService));
            }
        }

        #endregion

        #region "Public Properties (Engine Objects)"

        /// <summary>
        /// Game's Graphic Device Manager
        /// </summary>
        public GraphicsDeviceManager GraphicDeviceManager
        {
            get { return this.graphicsDeviceManager; }
        }

        /// <summary>
        /// Scene Manager
        /// </summary>
        public SceneManager SceneManager
        {
            get { return this.sceneManager; }
        }

        /// <summary>
        /// Input Manager
        /// </summary>
        public InputManager InputManager
        {
            get { return this.inputManager; }
        }

        /// <summary>
        /// Camera Manager
        /// </summary>
        public CameraManager CameraManager
        {
            get { return this.cameraManager; }
        }

        /// <summary>
        /// Canvas Manager
        /// </summary>
        public CanvasManager CanvasManager
        {
            get { return this.canvasManager; }
        }

        /// <summary>
        /// Physics Manager
        /// </summary>
        public PhysicsManager PhysicsManager
        {
            get { return this.physicsManager; }
        }

        /// <summary>
        /// Audio Manager
        /// </summary>
        public AudioManager AudioManager
        {
            get { return this.audioManager; }
        }

        /// <summary>
        /// Allow acces to the Writer
        /// </summary>
        public Debug.Console DebugConsole
        {
            get { return this.debugConsole; }
        }

        /// <summary>
        /// Resource Manager
        /// </summary>
        public ResourceManager ResourceManager
        {
            get { return this.resourceManager; }
        }

        /// <summary>
        /// Scale factor for virtual canvas
        /// </summary>
        public Vector2 VirtualCanvasScale
        {
            get { return this.virtualCanvasScale; }
        }

        /// <summary>
        /// Engine configuration object. 
        /// Note: Upcast to the game engine config type, 
        /// if you are using custom configuration classes
        /// </summary>
        public XEngineConfiguration EngineConfiguration
        {
            get { return this.engineConfiguration; }
        }

        /// <summary>
        /// GameState currently active
        /// </summary>
        public GameState CurrentGameState
        {
            get { return this.currentGameState; }
        }

        /// <summary>
        /// Render manager
        /// </summary>
        public RenderManager RenderManager
        {
            get { return this.renderManager; }
        }

        /// <summary>
        /// Material manager
        /// </summary>
        public MaterialManager MaterialManager
        {
            get { return this.materialManager; }
        }

        /// <summary>
        /// Particles manager
        /// </summary>
        public ParticlesManager ParticlesManager
        {
            get { return this.particlesManager; }
        }

        /// <summary>
        /// Player manager
        /// </summary>
        public PlayerManager PlayerManager
        {
            get { return this.playerManager; }
        }

        /// <summary>
        /// Storage Manager
        /// </summary>
        public StorageManager StorageManager
        {
            get { return this.storageManager; }
        }

        /// <summary>
        /// Network Manager
        /// </summary>
        public NetworkManager NetworkManager
        {
            get { return this.networkManager; }
        }

        /// <summary>
        /// Localization Manager
        /// </summary>
        public LocalizationManager LocalizationManager
        {
            get { return this.localizationManager; }
        }

        /// <summary>
        /// Is the game running in trial mode?
        /// </summary>
        public bool IsTrialMode
        {
            get
            {
                return Guide.IsTrialMode;
            }
        }

        #endregion

        #region "World Update"

        /// <summary>
        /// Time elapse speed control factor
        /// </summary>
        private float timeSpeed = 1.0f;

        /// <summary>
        /// Controls how fast/slow the time will elapse
        /// </summary>
        public float TimeSpeed
        {
            get { return this.timeSpeed; }
            set { this.timeSpeed = value; }
        }

        /// <summary>
        /// Launch the update cycle on all the updateable sub-systems
        /// </summary>
        protected override void Update(GameTime gameTime)
        {
            #region Begin Profiling Section
#if PROFILER
            using (IDisposable d = UpdateSection.Measure())
            {
#endif
            #endregion

            //get the elapsed time
            float elapsedTime = (float)(gameTime.ElapsedGameTime.TotalSeconds * timeSpeed);

            //if the loading screen is active, we just update it and return
            if (LoadingScreen.Working)
            {
                LoadingScreen.Update(elapsedTime);
                if (!LoadingScreen.LoadingFinished) return;
            }

            //update the storageManager
            this.storageManager.Update();

            //synchronize the physics scene and the logic scene from the results of the physics simulation
            if (this.engineConfiguration._Physics.enabled)
            {
                this.PhysicsManager.WaitForResults();
                this.PhysicsManager.UpdateWorld();
            }

            if (this.OnBeforeUpdate != null) OnBeforeUpdate(elapsedTime);

            //update the audio manager
            this.audioManager.Update(elapsedTime);

            //update the network manager
            this.networkManager.Update(elapsedTime);

            //update the debug console
            this.debugConsole.Update(elapsedTime);

            //update the managers that requires so
            this.inputManager.Update(elapsedTime);

            //update the logic world
            XWorld.Instance.Update(elapsedTime);

            //update the scene tree
            this.sceneManager.Update();

            //save the last elapsed time
            lastElapsedTime = elapsedTime;

            //load the pendant assets
            if (this.currentGameState != null)
            {
                this.resourceManager.LoadPendantResources(this.currentGameState.ContentManager);
            }

            if (this.OnAfterUpdate != null) OnAfterUpdate(elapsedTime);

            //Advance the physics simulation. The results will be available on the next tick
            if (this.engineConfiguration._Physics.enabled)
            {
                this.PhysicsManager.Simulate(elapsedTime);
            }

            //launch trial unlock event
            if (this.isTrialModeLastValue && !this.IsTrialMode)
            {
                if (this.OnTrialUnlock != null) this.OnTrialUnlock();
            }

            //call the base system
            base.Update(gameTime);

            #region End Profiling Section
#if PROFILER
        }
#endif
            #endregion
        }

        private float lastElapsedTime;

        public float LastElapsedTime
        {
            get { return lastElapsedTime; }
        }

        #endregion

        /// <summary>
        /// Set the recently activated gamestate
        /// </summary>
        internal void setCurrentGameState(GameState gameState)
        {
            this.currentGameState = gameState;
        }

        /// <summary>
        /// Stops the engine, and exits the game
        /// </summary>
        public void Stop()
        {
            //stops the game
            this.Exit();
        }

        /// <summary>
        /// Reset the device state for 3D content
        /// </summary>
        private void setDeviceStateDefault3D(GraphicsDevice device)
        {
            device.RenderState.AlphaBlendEnable = true;
            device.RenderState.SourceBlend = Blend.One;
            device.RenderState.DestinationBlend = Blend.DestinationAlpha;
            device.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
            device.SamplerStates[0].AddressV = TextureAddressMode.Wrap;
        }

        protected override void EndRun()
        {
            if (this.engineConfiguration._Physics.enabled)
            {
                physicsManager.Dispose();
            }
            if (LoadingScreen.ActiveScreen != null)
            {
                LoadingScreen.ActiveScreen.Dispose();
            }
            if (ConsoleComponent.Instance != null)
            {
                Engine.Instance.StorageManager.Save<ConsoleComponent>(
                    /*StorageManager.StorageDevices.PlayerDevice,*/
                    ConsoleComponent.Instance, 
                    "Console", 
                    "console.log");

            }
            base.EndRun();
        }

        #region "System Pop-Ups"

        /// <summary>
        /// Show a simple Pop Up
        /// </summary>
        public void ShowSimplePopUpMessage(string Title, string Msg, IEnumerable<String> options)
        {
            //get the gamepad of the default player
            PlayerIndex index = PlayerIndex.One;
            if (playerManager.DefaultPlayer != null) index = playerManager.DefaultPlayer.GamepadIndex;

            Guide.BeginShowMessageBox(index, Title, Msg, options, 0, MessageBoxIcon.Alert, popUpCallback, null);
        }

        /// <summary>
        /// Callback for popups
        /// </summary>
        private void popUpCallback(IAsyncResult ar)
        {
            Guide.EndShowMessageBox(ar);
        }

        #endregion

        /// <summary>
        /// Invoke OnBeforeUnload event
        /// </summary>
        internal void NotifyBeforeUnload(GameState prevGameState)
        {
            if (this.OnBeforeGamestateUnload != null) this.OnBeforeGamestateUnload(prevGameState);
        }

    }

}
